package com.bean.activiti.service.impl;

import com.bean.activiti.entity.VacTask;
import com.bean.activiti.entity.Vacation;
import com.bean.activiti.util.ActivitiUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.*;

/**
 * @author Gavin Lee
 * @version 1.0
 * @date 2020-09-18 12:26
 * @desc
 */
@Service
public class VacationService {

    private static final Logger LOGGER = LoggerFactory.getLogger(VacationService.class);

    @Resource
    private RuntimeService runtimeService;
    @Resource
    private IdentityService identityService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private RepositoryService repositoryService;


//    private static final String PROCESS_DEFINE_KEY = "vacationProcess";
    private static final String PROCESS_DEFINE_KEY = "studentVacationProcess";

   /* @PostConstruct
    public void initDeployment() {
        Deployment deployment2 = repositoryService.createDeployment().name("test")
                .addClasspathResource("./processes/vacation.bpmn")
                .deploy();
        System.out.println("Id:"+ deployment2.getId() + ";Name:" + deployment2.getName());

        Deployment deployment3 = repositoryService.createDeployment().name("test")
                .addClasspathResource("./processes/studentvaction.bpmn")
                .deploy();
        System.out.println("Id:"+ deployment3.getId() + ";Name:" + deployment3.getName());
    }*/


    public Object startVac(String userName, Vacation vac) {

        identityService.setAuthenticatedUserId(userName);
        // 开始流程
        ProcessInstance vacationInstance = runtimeService.startProcessInstanceByKey(PROCESS_DEFINE_KEY);
        // 查询当前任务
        Task currentTask = taskService.createTaskQuery().processInstanceId(vacationInstance.getId()).singleResult();
        // 申明任务
        taskService.claim(currentTask.getId(), userName);

        Map<String, Object> vars = new HashMap<>(4);
        vars.put("applyUser", userName);
        vars.put("days", vac.getDays());
        vars.put("reason", vac.getReason());

        taskService.complete(currentTask.getId(), vars);

        return true;
    }

    public Object myVac(String userName) {
        List<ProcessInstance> instanceList = runtimeService.createProcessInstanceQuery().startedBy(userName).list();
        List<Vacation> vacList = new ArrayList<>();
        for (ProcessInstance instance : instanceList) {
            Vacation vac = getVac(instance);
            vacList.add(vac);
        }
        return vacList;
    }

    private Vacation getVac(ProcessInstance instance) {
        Integer days = runtimeService.getVariable(instance.getId(), "days", Integer.class);
        String reason = runtimeService.getVariable(instance.getId(), "reason", String.class);
        Vacation vac = new Vacation();
        vac.setApplyUser(instance.getStartUserId());
        vac.setDays(days);
        vac.setReason(reason);
        Date startTime = instance.getStartTime(); // activiti 6 才有
        vac.setApplyTime(startTime);
        vac.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批");
        return vac;
    }


    public Object myAudit(String userName) {
        List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(userName)
                .orderByTaskCreateTime().desc().list();
//        / 多此一举 taskList中包含了以下内容(用户的任务中包含了所在用户组的任务)
//        Group group = identityService.createGroupQuery().groupMember(userName).singleResult();
//        List<Task> list = taskService.createTaskQuery().taskCandidateGroup(group.getId()).list();
//        taskList.addAll(list);
        List<VacTask> vacTaskList = new ArrayList<>();
        for (Task task : taskList) {
            VacTask vacTask = new VacTask();
            vacTask.setId(task.getId());
            vacTask.setName(task.getName());
            vacTask.setCreateTime(task.getCreateTime());
            String instanceId = task.getProcessInstanceId();
            ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
            Vacation vac = getVac(instance);
            vacTask.setVac(vac);
            vacTaskList.add(vacTask);
        }
        return vacTaskList;
    }

    public Object passAudit(String userName, VacTask vacTask) {
        String taskId = vacTask.getId();
        String result = vacTask.getVac().getResult();
        Map<String, Object> vars = new HashMap<>();
        vars.put("result", result);
        vars.put("auditor", userName);
        vars.put("auditTime", new Date());
        taskService.claim(taskId, userName);
        taskService.complete(taskId, vars);
        return true;
    }

    public Object myVacRecord(String userName) {
        List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(PROCESS_DEFINE_KEY).startedBy(userName).finished()
                .orderByProcessInstanceEndTime().desc().list();

        List<Vacation> vacList = new ArrayList<>();
        for (HistoricProcessInstance hisInstance : hisProInstance) {
            Vacation vacation = new Vacation();
            vacation.setApplyUser(hisInstance.getStartUserId());
            vacation.setApplyTime(hisInstance.getStartTime());
            vacation.setApplyStatus("申请结束");
            List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(hisInstance.getId()).list();
            ActivitiUtil.setVars(vacation, varInstanceList);
            vacList.add(vacation);
        }
        return vacList;
    }


    public Object myAuditRecord(String userName) {
        List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(PROCESS_DEFINE_KEY).involvedUser(userName).finished()
                .orderByProcessInstanceEndTime().desc().list();

        List<String> auditTaskNameList = new ArrayList<>();
        auditTaskNameList.add("学生填写申请");
        auditTaskNameList.add("教师审批");
        auditTaskNameList.add("教务处审批");
        auditTaskNameList.add("校长审批");

        List<Vacation> vacList = new ArrayList<>();
        for (HistoricProcessInstance hisInstance : hisProInstance) {
            List<HistoricTaskInstance> hisTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceId(hisInstance.getId()).processFinished()
                    .taskAssignee(userName)
                    .taskNameIn(auditTaskNameList)
                    .orderByHistoricTaskInstanceEndTime().desc().list();
            boolean isMyAudit = false;
            for (HistoricTaskInstance taskInstance : hisTaskInstanceList) {
                if (taskInstance.getAssignee().equals(userName)) {
                    isMyAudit = true;
                }
            }
            if (!isMyAudit) {
                continue;
            }
            Vacation vacation = new Vacation();
            vacation.setApplyUser(hisInstance.getStartUserId());
            vacation.setApplyStatus("申请结束");
            vacation.setApplyTime(hisInstance.getStartTime());
            List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(hisInstance.getId()).list();
            ActivitiUtil.setVars(vacation, varInstanceList);
            vacList.add(vacation);
        }
        return vacList;
    }
    /**
     * 删除流程实例
     * @param procinstId
     */
    public void deleteProcess(String procinstId) {
        deleteRuntimeProcess(procinstId);
        deleteHistoryRuntimeProcess(procinstId);
    }

    public String getProcessImage(String procinstId) {
        BufferedImage img = new BufferedImage(300, 150, BufferedImage.TYPE_INT_RGB);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String imgBase64Str = "";
        try {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(procinstId).singleResult();
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(historicProcessInstance.getProcessDefinitionId())//使用部署对象ID查询
                    .singleResult();
            List<Task> activeTasks=taskService.createTaskQuery().processDefinitionId(processDefinition.getId()).list();
            // 已执行的节点ID集合
            List<String> executedActivityIdList = new ArrayList<String>();
            List<String> highLines = new ArrayList<String>();
            List<String> highNodes = new ArrayList<String>();

            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
            for(Task tk:activeTasks){
                executedActivityIdList.add(tk.getTaskDefinitionKey());
                highLines.addAll(getHighLines(bpmnModel,tk.getTaskDefinitionKey()));
                highNodes.addAll(getHighNodes(bpmnModel,tk.getTaskDefinitionKey()));
            }
            // 获取流程图图像字符流
            InputStream imageStream = new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, "png",
                    highNodes, highLines, "宋体", "宋体", "宋体", null, 1.0);
            // 输出资源内容到相应对象
            byte[] b = new byte[1024];
            int len;
            while ((len = imageStream.read(b, 0, 1024)) != -1) {
                outputStream.write(b, 0, len);
            }
            imgBase64Str = "data:image/png;base64," + Base64.getEncoder().encodeToString(Objects.requireNonNull(outputStream.toByteArray()));
            imageStream.close();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return imgBase64Str;
    }


    /**
     * 删除部署信息
     */
    public void deleteDeployment(String deploymentId){
        repositoryService.deleteDeployment(deploymentId, false);
    }

    /**
     * 在 ACT_HI_ACTINST 表中找到对应流程实例的userTask节点 不包括startEvent
     * @param processInstanceId
     * @return
     */
    private List<HistoricActivityInstance> getHisUserTaskActivityInstanceList(String processInstanceId) {
        List<HistoricActivityInstance> hisActivityInstanceList = ((HistoricActivityInstanceQuery) historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).activityType("userTask")
                .finished().orderByHistoricActivityInstanceEndTime().desc())
                .list();
        return hisActivityInstanceList;
    }

    /**
     * 删除运行中流程实例
     * @param procinstId
     */
    private void deleteRuntimeProcess(String procinstId) {
        try {
            runtimeService.deleteProcessInstance(procinstId,"");
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * 删除历史中流程实例
     * @param procinstId
     */
    private void deleteHistoryRuntimeProcess(String procinstId) {
        try {
            historyService.deleteHistoricProcessInstance(procinstId);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * 获取高亮的线
     * @param bpmnModel
     * @param key
     * @return
     */
    private Set<String> getHighLines(BpmnModel bpmnModel, String key){
        FlowNode fl=(FlowNode) bpmnModel.getFlowElement(key);
        List<SequenceFlow> pvmTransitions = fl.getIncomingFlows();
        Set<String> highLines=new HashSet<>();
        for(SequenceFlow sf:pvmTransitions){
            highLines.add(sf.getId());
            if(StringUtils.isNotBlank(sf.getSourceRef())){
                highLines.addAll(getHighLines(bpmnModel,sf.getSourceRef()));
            }
        }
        return highLines;
    }
    /**
     * 获取高亮的线
     * @param bpmnModel
     * @param key
     * @return
     */
    private Set<String> getHighNodes(BpmnModel bpmnModel, String key){
        FlowNode fl=(FlowNode) bpmnModel.getFlowElement(key);
        List<SequenceFlow> sequenceFlows = fl.getIncomingFlows();
        Set<String> highNodes=new HashSet<>();
        highNodes.add(key);
        for(SequenceFlow sf:sequenceFlows){
            if(StringUtils.isNotBlank(sf.getSourceRef())){
                highNodes.addAll(getHighNodes(bpmnModel,sf.getSourceRef()));
            }
        }
        return highNodes;
    }
}
